﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Xml.Linq;

namespace DO.Workflows
{
    [DataContract]
    public abstract class ResultBase
    {
        [DataMember]
        public Guid ComponentID { get; set; }
        [DataMember]
        public Guid JobID { get; set; }
        [DataMember]
        public DateTime? FinishTime { get; set; }

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        protected ResultBase()
            : this(Guid.NewGuid(), Guid.NewGuid())
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="componentID"></param>
        /// <param name="jobTicketID"></param>
        protected ResultBase(Guid componentID, Guid jobTicketID)
        {
            this.ComponentID = componentID;
            this.JobID = jobTicketID;
            this.FinishTime = DateTime.Now;
        }
        #endregion

        #region xml
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public virtual XElement Serialize()
        {
            XElement xe = new XElement(
                "Output",
                new XAttribute("ComponentID", this.ComponentID.ToString()),
                new XAttribute("JobID", this.JobID.ToString()),
                new XAttribute("FinishTime", this.FinishTime.HasValue? this.FinishTime.ToString(): string.Empty));
            return xe;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="xe"></param>
        /// <returns></returns>
        public virtual ResultBase Instantiate(XElement xe)
        {
            ResultBase output = (ResultBase)Activator.CreateInstance(this.GetType());
            output.ComponentID = new Guid(xe.Attribute("ComponentID").Value);
            output.JobID = new Guid(xe.Attribute("JobID").Value);
            output.FinishTime = string.IsNullOrEmpty(xe.Attribute("FinishTime").Value)
                                    ? new DateTime?()
                                    : DateTime.Parse(xe.Attribute("FinishTime").Value);
            return output;
        }
        #endregion

        #region bindings
        /// <summary>
        /// 
        /// </summary>
        /// <param name="excludeMemberFromTypes"></param>
        /// <returns></returns>
        public virtual Dictionary<string, Type> GetBindableMemberNames(Type[] excludeMemberFromTypes)
        {
            Dictionary<string, Type> bindableMemberNames = new Dictionary<string, Type>();
            FieldInfo[] fields = this.GetType().GetFields();
            if (fields != null && fields.Length > 0)
            {
                foreach (FieldInfo field in fields)
                {
                    bindableMemberNames.Add(field.Name, field.FieldType);
                }
            }
            PropertyInfo[] props = this.GetType().GetProperties();
            if (props != null && props.Length > 0)
            {
                foreach (PropertyInfo prop in props)
                {
                    bindableMemberNames.Add(prop.Name, prop.PropertyType);
                }
            }

            if (excludeMemberFromTypes != null && excludeMemberFromTypes.Length > 0)
            {
                foreach (Type excludedType in excludeMemberFromTypes)
                {
                    FieldInfo[] exFields = excludedType.GetFields();
                    if (exFields != null && exFields.Length > 0)
                    {
                        foreach (FieldInfo field in exFields)
                        {
                            if (bindableMemberNames.ContainsKey(field.Name))
                                bindableMemberNames.Remove(field.Name);
                        }
                    }
                    PropertyInfo[] exProps = excludedType.GetProperties();
                    if (exProps != null && exProps.Length > 0)
                    {
                        foreach (PropertyInfo prop in exProps)
                        {
                            if (bindableMemberNames.ContainsKey(prop.Name))
                                bindableMemberNames.Remove(prop.Name);
                        }
                    }
                }
            }

            return bindableMemberNames;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public virtual object ReadMemberValue(string memberName)
        {
            FieldInfo fieldInfo = this.GetType().GetField(memberName);
            if (fieldInfo != null)
            {
                return fieldInfo.GetValue(this);
            }
            else
            {
                PropertyInfo propInfo = this.GetType().GetProperty(memberName);
                if (propInfo != null)
                {
                    return propInfo.GetValue(this, null);
                }
            }
            return null;
        }
        #endregion

        #region clone
        public object Clone()
        {
            ResultBase newOutput = (ResultBase)Activator.CreateInstance(this.GetType());
            PropertyInfo[] props = this.GetType().GetProperties();
            foreach (PropertyInfo prop in props)
            {
                if (prop.CanRead && prop.CanWrite)
                {
                    object propValue = prop.GetValue(this, null);
                    prop.SetValue(newOutput, propValue, null);
                }
            }
            return newOutput;
        }
        #endregion
    }
}
